#include "stm32f10x.h"
#include "Delay.h"
#include "DHT11.h"

uint8_t DHTData[5] = {0x00, 0x00, 0x00, 0x00, 0x00}; // 存储温湿度 校验和

void DHT11_IO_OUT(void)
{ // 温湿度模块输出函数
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_OD; // 推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

void DHT11_IO_IN(void)
{ // 温湿度模块输入函数
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

// 40位分为5个字节来处理
uint8_t DHT11_ReadByte()
{
    uint8_t temp;         // 存放读取到的8位数据
    uint8_t ReadData = 0; // 存放最终读取到的数据
    uint8_t retry    = 0; // 判断
    uint8_t i;
    for (i = 0; i < 8; i++) {
        while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0 && retry < 100)
        // 等待DHT11输出高电平 retry防止程序卡在这
        {
            Delay_us(1);
            retry++;
        }
        retry = 0;
        Delay_us(30);
        // 由于0代码高电平时间26~28us '1'代码高电平时间70us,延时30us可判断出是1,还是0
        temp = 0; // 先将寄存器清零
        // 开始判断1/0
        if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 1) { // 延时30us之后如果还是高电平，证明为1代码
            temp = 1;                                         // 将1存储
        }
        while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 1 && retry < 100)
        // 等待信号被拉低，跳出
        {
            Delay_us(1);
            retry++;
        }
        retry = 0;
        // 由于高位先出
        ReadData <<= 1;   // 数据左移1位 准备存放新得到的数据
        ReadData |= temp; // 新得到的数据放到最后1位
    }
    return ReadData;
}

// 完整工作时序 开始读取温湿度数据
uint8_t DHT_Read(void)
{
    uint8_t i;
    uint8_t retry = 0;
    DHT11_IO_OUT();                     // 先设置端口方向为输出
    GPIO_ResetBits(GPIOB, GPIO_Pin_11); // 端口数据拉低
    Delay_ms(18);                       // 延时18ms 时序要求
    GPIO_SetBits(GPIOB, GPIO_Pin_11);   // 端口数据拉高
    Delay_us(40);                       // 延时40us
    DHT11_IO_IN();                      // 设置为输入
    Delay_us(20);                       // 延时20us 等待电平稳定

    if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0) // 如果读取到低电平 证明DHT11响应
    {
        while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0 && retry < 100) // 等待变高电平
        {
            Delay_us(1);
            retry++;
        }
        retry = 0;

        // 等待变成低电平 开始传输数据
        while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 1 && retry < 100) {
            Delay_us(1);
            retry++;
        }
        retry = 0;

        for (i = 0; i < 5; i++) // 循环5次将40位读出
        {
            DHTData[i] = DHT11_ReadByte(); // 读出1个字节 放在Data的5个数组里
        }
        Delay_us(50); // 最后延时等待50us
    }

    // 校验 Data已设置为8位变量
    if (DHTData[4] == DHTData[0] + DHTData[1] + DHTData[2] + DHTData[3])
    // 前4个数据和的末8位要和第5个数据相等，才算读取正确
    {
        return 1; // 校验正确，返回1
    } else
        return 0; // 校验错误，返回0
}
